/*
 * Copyright (c) 2008-2023, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.huaweicloud;

import com.hazelcast.internal.util.StringUtil;
import com.hazelcast.logging.ILogger;
import com.hazelcast.logging.Logger;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * Fetches Token from IAM
 * <a href="https://support.huaweicloud.com/api-iam/iam_30_0001.html"></a>
 */
class HwAuthenticator {
    private static final ILogger LOGGER = Logger.getLogger(HwAuthenticator.class);
    private static final String TOKEN_URL = "https://iam.myhuaweicloud.com/v3/auth/tokens";
    private static final String SEPARATOR = System.lineSeparator();
    private static final int SUCCESS = 201;
    private static final int FAILED = 400;
    private final String endpoint;

    HwAuthenticator() {
        this(TOKEN_URL);
    }

    /**
     * For test purposes only.
     */
    HwAuthenticator(String endpoint) {
        this.endpoint = endpoint;
    }

    String refreshAccessToken(String iamDomain, String iamUser, String iamPassword, String projectId) {
        if (StringUtil.isNullOrEmpty(iamDomain) || StringUtil.isNullOrEmpty(iamUser)
                || StringUtil.isNullOrEmpty(iamPassword)) {
            LOGGER.warning("Error while fetching token from IAM,please make sure your iam config is filled");
            return "";
        }

        return callService(body(iamDomain, iamUser, iamPassword, projectId));

    }

    // request body
    private String body(String iamDomain, String iamUser, String iamPassword, String projectId) {
        return String.format(
                "{" + SEPARATOR + "    \"auth\": {" + SEPARATOR + "        \"identity\": {" + SEPARATOR
                        + "            \"methods\": [" + SEPARATOR + "                \"password\"" + SEPARATOR
                        + "            ]," + SEPARATOR + "            \"password\": {" + SEPARATOR
                        + "                \"user\": {" + SEPARATOR + "                    \"domain\": {" + SEPARATOR
                        + "                        \"name\": \"%s\"" + SEPARATOR + "                    }," + SEPARATOR
                        + "                    \"name\": \"%s\"," + SEPARATOR
                        + "                    \"password\": \"%s\"" + SEPARATOR + "                }" + SEPARATOR
                        + "            }" + SEPARATOR + "        }," + SEPARATOR + "        \"scope\": {" + SEPARATOR
                        + "            \"project\": {" + SEPARATOR + "                \"id\": \"%s\"" + SEPARATOR
                        + "            }" + SEPARATOR + "        }" + SEPARATOR + "    }" + SEPARATOR + "}",
                iamDomain, iamUser, iamPassword, projectId);
    }

    // Use the HttpURLConnection class to send HTTP requests and receive HTTP responses,
    // and perform different operations according to the HTTP response code returned by the HttpURLConnection object
    private String callService(String body) {
        try {
            URL conUrl = new URL(endpoint);
            URLConnection con = conUrl.openConnection();
            if (con instanceof HttpURLConnection) {
                HttpURLConnection connection = (HttpURLConnection) con;

                connection.setRequestMethod("POST");
                if (body != null) {
                    byte[] bodyData = body.getBytes(StandardCharsets.UTF_8);
                    connection.setDoOutput(true);
                    connection.setRequestProperty("charset", "utf-8");
                    connection.setRequestProperty("Content-Length", Integer.toString(bodyData.length));
                    DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());

                    try {
                        outputStream.write(bodyData);
                        outputStream.flush();
                    } catch (IOException e) {
                        outputStream.close();
                        LOGGER.warning(e);
                    }
                }
                int responseCode = connection.getResponseCode();
                // success response
                if (responseCode == SUCCESS) {
                    Map<String, List<String>> map = connection.getHeaderFields();
                    if (map.get("X-Subject-Token") != null) {
                        return map.get("X-Subject-Token").get(0);
                    }
                } else if (responseCode == FAILED) {
                    LOGGER.warning("The request body is invalid");
                } else {
                    LOGGER.warning("The iamUser or iamPassword is wrong");
                }
            }
        } catch (IOException e) {
            LOGGER.warning(e);
        }

        return "";
    }
}
